poi-tl导出word;自定义列表序号和表格宽度,表格合并,自定义标题,更新目录

poi-tl 入门示例

可参照poi-tl 官方api文档:poi-tl
引入maven 后

							<dependency>
							  <groupId>com.deepoove</groupId>
							  <artifactId>poi-tl</artifactId>
							  <version>1.11.0</version>
							</dependency>

1.准备word模板

word模板的内容

2.代码

  public class PoiTest {
   public static void main(String[] args) throws IOException {
        //要写入模板的数据
        Map<String,Object> exampleData = new HashMap<>();
        exampleData.put("username","admin");
        exampleData.put("password","123456");
        FileInputStream inputStream = new FileInputStream("D:/test_word/example1.docx");
        XWPFTemplate template = XWPFTemplate.compile(inputStream)
                .render(exampleData);
        //文件输出流
        FileOutputStream out = new FileOutputStream("D:/test_word/example1.docx");
        template.write(out);
        out.flush();
        out.close();
        template.close();
    }
}

自定义列表序号

1. poi-tl 支持的序号列表,直接使用文档种说明即可

poi-tl支持的序号列表

2. 要是没有,可以利用NumberingFormat类去生成指定的样式;例如生成 a) b)的序号

要生成的列表序号样式

3. 同样按照官方api指定{{*var}}后

4. 具体代码如下

public class test {


    public static void main(String[] args) throws IOException {
        //要写入模板的数据
        Map<String, Object> datas1 = new HashMap<String, Object>();
        //列表集合
        List<String> varList = Arrays.asList("序号1",
                "序号2");

        //自定义序号的样式为  a)  b)  c)
        NumberingFormat.Builder builder = NumberingFormat.
                builder("%{0})") //%{0}) 可以指定自己需要的样式
                .withNumFmt(NumFormat.LOWER_LETTER);  //小写字母
        NumberingFormat numberingFormat = builder.build(0);
        Numberings.NumberingBuilder of = Numberings.of(numberingFormat);//a)  b)  c)
        //列表 数据赋值
        varList.forEach(s -> of.addItem(s));
        NumberingRenderData numberingRenderData = of.create();
        datas1.put("var", numberingRenderData);
        XWPFTemplate.compile("D:/test_word/test.docx")
                .render(datas1).writeToFile("D:/test_word/test1.docx");
    }
}

自定义表格宽度和表格合并

poi-tl导出的word表格默认宽度是自适应的,若是想要指定列的宽度增加,可以采用如下方式:
最终实现的word表格

word表格

1. 在word模板文件中,生成表格位置添加标签:{{#check_table}}

2. 具体整体代码如下

 public class test {


    public static void main(String[] args) throws IOException {

        Map<String, Object> datas = new HashMap<String, Object>();
        // create table
        //word的 表格
        List tableList = new ArrayList<>();
        tableList.add("数据1");
        tableList.add("数据2");
        tableList.add("数据3");
        tableList.add("数据4");
        tableList.add("数据5");
        tableList.add("数据6");
        //验证类型集合
        List<String> typeList = Arrays.asList("MC0",
                "MC1",
                "MC2",
                "MC3",
                "MC4",
                "MC5",
                "MC6",
                "MC7",
                "MC8",
                "MC9");
        //表头是两行
        //第一行数据是固定内容 "序号", "章节号", "项目", "内容", "验证方法", "备注"
        List<String> headerCellListTemp1 = Arrays.asList("序号", "章节号", "项目", "内容", "验证方法");
        List<String> headerCellList1 = new ArrayList<>(headerCellListTemp1);
        //验证方法,可能是多列数据  第一列是验证方法,后面是赋空字符串,用于合并单元格;
        //数据类型:验证方法 "" "" ""
        //验证类型长度
        int typeListSize = typeList.size();
        for (int k = 0; k < typeListSize - 1; k++) {
            headerCellList1.add(" "); //赋空字符串
        }
        headerCellList1.add("备注");

        String[] headerCell1 = headerCellList1.toArray(new String[headerCellList1.size()]);

        RowRenderData header1 = Rows.of(headerCell1).center().create();


        //headerCell2 :验证方法对应列,是动态内容
        List<String> headerCellListTemp2 = Arrays.asList("序号", "章节号", "项目", "内容");
        List<String> headerCellList2 = new ArrayList<>(headerCellListTemp2);
        // 验证方法对应列
        headerCellList2.addAll(typeList);
        //备注对应的列 赋空值
        headerCellList2.add(" ");

        String[] headerCell2 = headerCellList2.toArray(new String[headerCellList2.size()]);
        RowRenderData header2 = Rows.of(headerCell2).center().create();
        //表格 列的数量
        int cellLength = headerCell1.length;

        //无数据只有表头数据 tableList 无值
        if (CollectionUtil.isEmpty(tableList)) {
            RowRenderData[] RowRenderDataHeader = new RowRenderData[2];
            RowRenderDataHeader[0] = header1;
            RowRenderDataHeader[1] = header2;

            TableRenderData check_table = setTableRenderDataAndColWidth(RowRenderDataHeader, cellLength);


            // table 只有表头数据
            datas.put("check_table", check_table);
        } else {
            int length = 2 + tableList.size();
            //行数据 数组
            RowRenderData[] RowRenderData = new RowRenderData[length];

            RowRenderData[0] = header1;
            RowRenderData[1] = header2;

            for (int i = 0; i < tableList.size(); i++) {

                String index = Integer.toString(i + 1);
                List<String> tempList = Arrays.asList("A", "B", "C",
                        "D", "E", "F", "G", "H", "I", "G");

                String[] cellString = new String[cellLength];
                cellString[0] = index; //序号
                cellString[1] = "5.1.1";//标题序号
                cellString[2] = "项目"; // 项目
                cellString[3] = "说明"; //内容 : 描述
                //验证方法对应数据赋值
                if (headerCell2.length > 4) {
                    for (int j = 0; j < tempList.size(); j++) {
                        cellString[j + 4] = tempList.get(j);
                    }
                }

                //备注
                cellString[cellLength - 1] = "备注";
                RowRenderData rowRenderData = Rows.of(cellString).
                        center().create();
                //行数据赋值
                RowRenderData[2 + i] = rowRenderData;

            }
            TableRenderData check_table = setTableRenderDataAndColWidth(RowRenderData, cellLength);
            // table
            datas.put("check_table", check_table);


        }
        XWPFTemplate.compile("D:/test_word/test.docx")
                .render(datas).writeToFile("D:/test_word/test1.docx");
    }

    /**
     * 表格赋值,
     * 设置列宽和合并单元格
     *
     * @param rowRenderDataArray
     * @param cellLength
     * @return
     */
    private static TableRenderData setTableRenderDataAndColWidth(RowRenderData[] rowRenderDataArray, Integer cellLength) {
        //table赋值set方法需要list
        List<RowRenderData> RowRenderDataList = Arrays.asList(rowRenderDataArray);
        //设置列宽:
        double[] colWidthsCm = new double[cellLength];
        for (int i = 0; i < cellLength; i++) {
            // "章节号", "项目", "内容" 设置为 2
            if (i == 1 || i == 2 || i == 3) {
                colWidthsCm[i] = 2D;
            } else {
                colWidthsCm[i] = 1D;
            }
        }
        //18.450000762939453D A4纸张
        TableRenderData check_table = Tables.ofWidth(18.450000762939453D, colWidthsCm).center().create();
        check_table.setRows(RowRenderDataList);
        //合并单元格
        MergeCellRule.MergeCellRuleBuilder mergeCellRuleBuilder = mergeCell(cellLength);
        check_table.setMergeRule(mergeCellRuleBuilder.build());

        return check_table;
    }
    /**
     * 表格 合并单元格
     *
     * @param cellLength
     * @return
     */
    private static MergeCellRule.MergeCellRuleBuilder mergeCell(Integer cellLength) {
        /**
         * 设置表格合并规则 从0开始
         * 1.起始行 MergeCellRule.Grid.of(i, j) i: 行 j: 列
         * 2.结束行 MergeCellRule.Grid.of(i, j) i: 行 j: 列
         */
        //合并单元格
        //合并到【备注】前一列
        MergeCellRule.MergeCellRuleBuilder mergeCellRuleBuilder = MergeCellRule.builder();
        mergeCellRuleBuilder.map(MergeCellRule.Grid.of(0, 0), MergeCellRule.Grid.of(1, 0));//序号合并
        mergeCellRuleBuilder.map(MergeCellRule.Grid.of(0, 1), MergeCellRule.Grid.of(1, 1));//章节号合并
        mergeCellRuleBuilder.map(MergeCellRule.Grid.of(0, 2), MergeCellRule.Grid.of(1, 2));//项目合并
        mergeCellRuleBuilder.map(MergeCellRule.Grid.of(0, 3), MergeCellRule.Grid.of(1, 3));//内容合并
        mergeCellRuleBuilder.map(MergeCellRule.Grid.of(0, 4), MergeCellRule.Grid.of(0, cellLength - 2));//验证类型合并 第一行
        mergeCellRuleBuilder.map(MergeCellRule.Grid.of(0, cellLength - 1), MergeCellRule.Grid.of(1, cellLength - 1));//备注合并
        return mergeCellRuleBuilder;
    }

}

3. 设置表格宽度 setTableRenderDataAndColWidth

  /**
     * 表格赋值,
     * 设置列宽和合并单元格
     *
     * @param rowRenderDataArray
     * @param cellLength
     * @return
     */
    private static TableRenderData setTableRenderDataAndColWidth(RowRenderData[] rowRenderDataArray, Integer cellLength) {
        //table赋值set方法需要list
        List<RowRenderData> RowRenderDataList = Arrays.asList(rowRenderDataArray);
        //设置列宽:
        double[] colWidthsCm = new double[cellLength];
        for (int i = 0; i < cellLength; i++) {
            // "章节号", "项目", "内容" 设置为 2 ;这几列宽度大
            if (i == 1 || i == 2 || i == 3) {
                colWidthsCm[i] = 2D;
            } else {
                colWidthsCm[i] = 1D;
            }
        }
        //18.450000762939453D A4纸张
        TableRenderData check_table = Tables.ofWidth(18.450000762939453D, colWidthsCm).center().create();
        check_table.setRows(RowRenderDataList);
        //合并单元格
        MergeCellRule.MergeCellRuleBuilder mergeCellRuleBuilder = mergeCell(cellLength);
        check_table.setMergeRule(mergeCellRuleBuilder.build());

        return check_table;
    }

4. 表格合并方法 mergeCell

 /**
     * 表格 合并单元格
     *
     * @param cellLength
     * @return
     */
    private static MergeCellRule.MergeCellRuleBuilder mergeCell(Integer cellLength) {
        /**
         * 设置表格合并规则 从0开始
         * 1.起始行 MergeCellRule.Grid.of(i, j) i: 行 j: 列
         * 2.结束行 MergeCellRule.Grid.of(i, j) i: 行 j: 列
         */
        //合并单元格
        //合并到【备注】前一列
        MergeCellRule.MergeCellRuleBuilder mergeCellRuleBuilder = MergeCellRule.builder();
        mergeCellRuleBuilder.map(MergeCellRule.Grid.of(0, 0), MergeCellRule.Grid.of(1, 0));//序号合并 上下行合并
        mergeCellRuleBuilder.map(MergeCellRule.Grid.of(0, 1), MergeCellRule.Grid.of(1, 1));//章节号合并
        mergeCellRuleBuilder.map(MergeCellRule.Grid.of(0, 2), MergeCellRule.Grid.of(1, 2));//项目合并
        mergeCellRuleBuilder.map(MergeCellRule.Grid.of(0, 3), MergeCellRule.Grid.of(1, 3));//内容合并
        mergeCellRuleBuilder.map(MergeCellRule.Grid.of(0, 4), MergeCellRule.Grid.of(0, cellLength - 2));//验证类型合并 第一行
        mergeCellRuleBuilder.map(MergeCellRule.Grid.of(0, cellLength - 1), MergeCellRule.Grid.of(1, cellLength - 1));//备注合并
        return mergeCellRuleBuilder;
    }

自定义标题

标题可以采用原生poi生成,这里实现利用的是poi-tl的Markdown插件中的标题;
最终生成的样式如下
在这里插入图片描述

1. word模板内容如下

{{?comment}}
Heading1简介
Heading2简介
Heading3简介
Heading4简介
Heading5简介
{{/comment}}
{{md}}

黑色内容是word模板上的内容
在这里插入图片描述 ## 2. md文件内容如下
在这里插入图片描述

3. 需要生成的标题按照md文件格式拼接为字符串即可

代码如下:

public class test {


    public static void main(String[] args) throws IOException {
        //要写入模板的数据
        Map<String, Object> datas = new HashMap<String, Object>();
        //标题的数据  需要按照md文件,标题的格式拼接数据
        MarkdownRenderData code = new MarkdownRenderData();
//        byte[] bytes = Files.readAllBytes(Paths.get("src/test/resources/markdown/basic1.md"));
//        String mkdn = new String(bytes);
        String mkdn = "### Heading 31\n" +
                "#### Heading 41\n" +
                "### Heading 32\n" +
                "#### Heading 42";
        code.setMarkdown(mkdn);

        MarkdownStyle style = MarkdownStyle.newStyle();
        style.setShowHeaderNumber(true); //设置标题序号
        code.setStyle(style);

        //标题的数据
        datas.put("md", code);
        Configure config = Configure.builder().bind("md", new MarkdownRenderPolicy()).build();
        XWPFTemplate.compile("src/test/resources/template_finnish/test_title.docx", config)
                .render(datas)
                .writeToFile("src/test/resources/out/out_markdown2.docx");
    }

生成标题其他注意点

1. 自己指定生成标题序号开始位置:重写DocumentVisitor.class 类

下面的内容是修改后的内容:
直接标题赋值时指定值:在这里插入图片描述
或者是修改原生的getHeaderNumber() 方法

   private String getHeaderNumber(int level) {
        if (level == 1) return "";
        String str = StringUtils.join(Arrays.
                copyOfRange(headerNumberArray, 2, headerNumberArray.length), '.');
        String substring = str.substring(0, (level - 1) * 2 >= str.length() ? str.length() : (level - 1) * 2);
        //目前是 5.2.1.
        //去除标题序号的.    保持和文档上一致 类似这种5.2.1
        if (StringUtils.isNotEmpty(substring)) {
            substring = substring.substring(0, substring.length() - 1);
        }

        return substring + " ";
    }

2. 自动生成标题的样式,也可以按照自己想要的方式设置

方式是修改DocumentVisitor 类中的visit()方法
代码如下:展示的代码都是自己修改后的

   @Override
    public void visit(Heading heading) {
        int level = heading.getLevel();
        resetHeaderNumberArray(level);

        ParagraphBuilder paraOf = Paragraphs.of().styleId(String.valueOf(level)).left().allowWordBreak();
        if (style.isShowHeaderNumber()) {
            paraOf.addText(Texts.of(getHeaderNumber(level)).style(getStyle(level)).create());
        }
        DocumentRenderData renderData = parseNode(heading);
        if (!renderData.getContents().isEmpty()) {
            ParagraphRenderData headerParagraph = (ParagraphRenderData) renderData.getContents().get(0);
           // paraOf.addParagraph(headerParagraph);
           // paraOf.addText(Texts.of("").bookmark(evalText(headerParagraph)).create());
            paraOf.addText(Texts.of(evalText(headerParagraph)).style(getStyle(level)).create());
        }
        of.addParagraph(paraOf.create());
    }

/**
     * 自定义标题的样式;
     * 不使用该方法,标题样式都是标签{{md}}的样式
     *
     * @param level
     * @return
     */

    private Style getStyle(int level) {
        Style style = null;
        switch (level) {
            case 2: //标题1
                style = new Style("宋体", 22D); //二号字体
                break;
            case 3: //标题2
                style = new Style("宋体", 16D);//三号字体
                break;
            case 4: //标题3
                style = new Style("宋体", 14D);//四号字体
                break;
            //其他都是4号字体
            default:
                style = new Style("宋体", 14D);//二号字体
                break;
        }
        return style;
    }

上述代码中,getStyle方法用于指定自己想要修改的样式

更新目录 (wps 不支持,只支持office)

网上搜到的方式,直接采用该方法,可以生成并且更新目录

转载链接如下:
转载生成poi目录

poi-tl的方式

word模板添加标签 {{TOC}}

在这里插入图片描述

代码如下

public class AutoMenuWordPoi {


    public static void main(String[] args) throws IOException, InvalidFormatException {


        //要写入模板的数据
        Map<String, Object> datas = new HashMap<String, Object>();
        datas.put("TOC", "TOC");
        Configure config = Configure.builder().bind("TOC", new TOCRenderPolicy()).build();


        XWPFTemplate.compile("src/test/resources/out/out_markdownToc_poi.docx", config)
                .render(datas)
                .writeToFile("src/test/resources/out/out_menu1.docx");



    }


}

也可以word已经生成目录,后面只是修改了添加标题,去更新目录

采用如下代码也可以:

public class AutoMenuWord2 {
    private static final String HEADING1 = "1";

    public static void main(String[] args) throws IOException, InvalidFormatException {


        XWPFDocument doc = new XWPFDocument(new FileInputStream("src/test/resources/out/out_markdown2.docx"));
        doc.enforceUpdateFields(); // 更新目录




        doc.write(new FileOutputStream("src/test/resources/out/out_markdown3.docx"));
        doc.close();


    }

}

  • 5
    点赞
  • 76
    收藏
    觉得还不错? 一键收藏
  • 22
    评论
poi-tl是一个基于POIJava模板引擎,可以用于导出Word文档。在poi-tl中,可以使用foreach指令实现循环,并根据需要循环输出列表行和列。 在poi-tl中,可以将需要循环的数据存储在一个List对象中,然后使用foreach指令进行循环遍历。假设我们有一个List<RowData>对象,其中RowData是自定义的类,用于表示每一行的数据。RowData类中可以包含一些属性,代表每一行中的不同列。 首先,我们需要在Word模板中使用foreach指令,来循环输出列表行和列。可以使用标签${foreach items=listVar item=rowVar}和${end}将需要循环的部分包围起来。其中listVar是存储数据的List对象的名称,rowVar是循环过程中每一行数据的临时变量名。 然后,在foreach指令中,可以使用${rowVar.property}的方式获取每一行的属性值,来完成对列表行和列的输出。property代表RowData类中的某一个属性的名称。 最后,使用poi-tl提供的模板渲染引擎,将数据填充到Word模板中,生成目标Word文档。 综上所述,利用poi-tl可以很方便地循环输出列表行和列。首先需要准备好存储数据的List对象,并在Word模板中使用foreach指令进行循环遍历。然后,在循环过程中使用${rowVar.property}的方式获取每一行的属性值,完成对列表行和列的输出。最终,使用poi-tl提供的模板渲染引擎将数据填充到Word模板中,生成目标Word文档。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 22
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值